/**
 * SINDB2XHTML project
 *  This utility converts docbook slides into XHTML
 * https://code.google.com/p/sindb2xhtm
 */
package cz.muni.fi.sindb2xhtml;


import java.io.File; 
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;

import com.icl.saxon.StyleSheet;

import cz.muni.fi.sindb2xhtml.utils.FileUtils;

/**
 * The Sindb2xhtml class
 *  provides functionality for the transformation  
 *  of docbook slides into xhtml 1.0
 *
 */
public class Sindb2xhtml {

	/**
	 *path to "style.css" (loaded from properties file
	 */
	private String styleCssFile = "/files/style.css";

	/**
	 * path to docbook slides xml file - file to be converted into xhtml
	 * (set on commandline)
	 */
	private String sourceXMLFile = "";

	/**
	 * path to /docbook/slides/xhtml/docbook.xsl (loaded from properties file)
	 */
	private String sourceXSLFile = "/docbook/slides/xhtml/default.xsl";

	//output can be generated ONLY to actual work dir (blame saxon)
	private String actualWorkDir = "./";

	/**
	 * CTOR
	 * @throws Sindb2xhtmlException when someting bad happens
	 */
	public Sindb2xhtml(final String[] args) throws Sindb2xhtmlException {

		//process command line arguments
		processArgs(args);
	
		try {
			final String actualDirPath = System.getProperty("user.dir");
			if (actualDirPath == null)
			{
			   throw new Sindb2xhtmlException("Cannot resolve actual working directory.");
			}
			final File actualDir = new File(actualDirPath);
						
			actualWorkDir = actualDir.getCanonicalPath();
		} catch (IOException e) {
			throw new Sindb2xhtmlException(e);
		}
	}

	/**
	 * Processes arguments list
	 *  -i [source docbook slides.xml]
	 *  -h help
	 * @param args command-line arguments
	 * @throws Sindb2xhtmlException when file read error occurs
	 */
	private void processArgs(final String[] args) throws Sindb2xhtmlException	{
			// -i switch set?
	        boolean sourceSet = false;
	        
	        // -h switch set? 
			boolean helpSet = false;
			
			// error occured?
			boolean fail = false;
			
			// argument list position
            int argNo = 0;
            
			while (argNo < args.length) { //loop over all arguments 

				// Set the source xml file here
				if (args[argNo].equals("-i") && (args.length >= argNo + 2)) {

				    if (sourceSet) {
				        fail  = true; // trying to re-set source file => fail
				    }
				    else { 
				        // Get the absolute path of the source xml file
				        sourceXMLFile = args[++argNo];
						if (sourceXMLFile == null || sourceXMLFile.length() == 0) {
							throw new Sindb2xhtmlException("Input XML file was not set.");
						}
						try {
							File xmlFile = new File(sourceXMLFile);
							if (!xmlFile.exists())
							{
								throw new Sindb2xhtmlException("Failed to open input file " + sourceXMLFile + ".");
							}
							sourceXMLFile = xmlFile.getCanonicalPath(); //path to source file
						} catch (IOException e) {
							throw new Sindb2xhtmlException("Failed to get the path of input file " + sourceXMLFile + ".",e);
						}
						
						sourceSet = true; //source file has been set
					}
				}
				
				//user needs help?
				if (args[argNo].equals("-h"))
				{
					helpSet = true;
				}
				
				argNo++; //next command-line argument
			}

			//invalid arguments / need help?
			if (args.length < 2 || fail || !sourceSet || helpSet) 
			{
				printHelp();
				System.exit(0);				
			}	
	}
	
	/**
	 * Displays short manual
	 */
	private void printHelp() {
		System.out.println("converts docbook/slides into XHTML");
		System.out.println("expected command-line arguments:");
		System.out.println("\t-i [path to source XML file (docbook slides)]");				
		System.out.println("\t-h Help");
	}

	

	/**
	 * The entry point of this application
	 * @param args command line args
	 */
	public static void main(String[] args) {

	    System.out.println("SINDB2XHTML 2011");
	
		Sindb2xhtml sindb2xhtml = null;
		try {
			sindb2xhtml = new Sindb2xhtml(args);
		} catch (Sindb2xhtmlException e1) {
			System.out.println("Failed to transform the docbook slidess.\n" + e1.getLocalizedMessage());
		    e1.printStackTrace();
			System.exit(2);
		}
		try { //run the transformation
			sindb2xhtml.xsltTransformation();
		} catch (Sindb2xhtmlException e) {
			e.printStackTrace();
			System.exit(4);			
		}
	}
	
	/**
	 * Transforms file set in processArgs
	 * @throws Sindb2xhtmlException
	 */
	public void xsltTransformation() throws Sindb2xhtmlException
	{
		System.out.println("xslt in use:\t" + sourceXSLFile);
		System.out.println("input:\t" + sourceXMLFile);

		
		xsltTransformation(sourceXMLFile); //transform!

		copyNecessaryFiles(actualWorkDir); //copy style.css into target dir
	}
	
	/**
	 * Transforms file set in processArgs
	 * @param inXSLFileName path to docbook.xsl
	 * @throws Sindb2xhtmlException
	 */	
	private void xsltTransformation (final String inputXMLFilePath) throws Sindb2xhtmlException
	{
		final File inputXMLFile = new File(inputXMLFilePath);
		final InputStream inputXSLStream = getClass().getResourceAsStream("/docbook/slides/xhtml/default.xsl");		

		
		final StreamSource inputXSLSource = new StreamSource(inputXSLStream); 

		//Saxon bug work-around (<xsl:import> relative reference)
        final URL url = getClass().getResource("/docbook/slides/xhtml/");
        inputXSLSource.setSystemId(url.toExternalForm());
        
        //Run it!
		xsltTransformation( new StreamSource(inputXMLFile), inputXSLSource);
	}
	
	/**
	 * Transforms file set in processArgs
	 * @param inXMLsrc input docbook slides source 
	 * @param inXSLsrc input defualt.xsl source
	 * @throws Sindb2xhtmlException when something doesn't work as expected
	 *         ( 
	 */
	private void xsltTransformation(final Source inXMLsrc,final Source inXSLsrc) throws Sindb2xhtmlException
	{		
		// temporary file - needed for transformation
		File tmp = new File(System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") + "sindb2xhtml.tmp");
		
		
		
		TransformerFactory factory = TransformerFactory.newInstance();

		Templates templates = null;
		
		try {
			templates = factory.newTemplates(inXSLsrc);
		} catch (TransformerConfigurationException e) {
			throw new Sindb2xhtmlException("Failed to compile input sources.",e);
		}
		
		StyleSheet stylesheet = new StyleSheet();

		try {
			stylesheet.processFile(inXMLsrc, templates, tmp, null);
			tmp.deleteOnExit();
		} catch (TransformerException e) {
			throw new Sindb2xhtmlException(e);
		}
	}

	/**
	 * Copies necessary files into target directory
	 * @param targetDir target directory
	 * @throws Sindb2xhtmlException when copying fails 
	 */
	private void copyNecessaryFiles(final String targetDir) throws Sindb2xhtmlException {
		
			InputStream cssFileStream = getClass().getResourceAsStream(styleCssFile);
			FileUtils.copyfile(cssFileStream, targetDir + "/style.css");	
	}

    public String getStyleCssFile() {
        return styleCssFile;
    }

    public void setStyleCssFile(String styleCssFile) {
        this.styleCssFile = styleCssFile;
    }

    public String getSourceXMLFile() {
        return sourceXMLFile;
    }

    public void setSourceXMLFile(String sourceXMLFile) {
        this.sourceXMLFile = sourceXMLFile;
    }

    public String getSourceXSLFile() {
        return sourceXSLFile;
    }

    public void setSourceXSLFile(String sourceXSLFile) {
        this.sourceXSLFile = sourceXSLFile;
    }

    public String getActualWorkDir() {
        return actualWorkDir;
    }

    public void setActualWorkDir(String actualWorkDir) {
        this.actualWorkDir = actualWorkDir;
    }

}
